
#pragma once

#include "CxIoSevice.hpp"
#include <vector>
#include <boost\bind.hpp>
#include <boost\signals2.hpp>

using	std::vector;
using	boost::noncopyable;
using	boost::function;


class CxSerialPort : public noncopyable
{
	//type
	typedef unsigned char				byte;
	typedef unsigned char*				bytes;

	// function define
	typedef boost::system::error_code	ERRCODE;
	typedef function<void(bool)>			_FinishHandler;
	typedef 
			boost::signals2::signal<void(const ERRCODE &)>	
										_ErrorSignal;

	typedef boost::asio::serial_port	_SerialPort;
	//Option	
	typedef _SerialPort::baud_rate				baud_rate;
	typedef _SerialPort::character_size			char_size;
	typedef _SerialPort::parity					parity;
	typedef _SerialPort::stop_bits				stop_bits;
	
public:
	//Option Type
	typedef _SerialPort::parity::type			t_parity;
	typedef _SerialPort::stop_bits::type		t_stop_bits;
	enum t_baud_rate
	{
		baud_rate_9600=9600,
		baud_rate_19200=19200,
		baud_rate_28800=28800,
		baud_rate_38400=38400,
		baud_rate_43000=43000,
		baud_rate_56000=56000,
		baud_rate_57600=57600,
		baud_rate_115200=115200,
		baud_rate_128000=128000,
		baud_rate_256000=256000,
	};
	enum t_char_size
	{
		char_8 = 8,
		char_7 = 7,
		char_6 = 6
	};

public:
	//constructor
	CxSerialPort(const std::string &,t_baud_rate  rate=baud_rate_9600	,
				t_char_size charsize=char_8,t_parity par=t_parity::none ,
				t_stop_bits stop=t_stop_bits::one
				);

	CxSerialPort();
	~CxSerialPort();


/* handle function */
public:
	bool		open(const std::string &, t_baud_rate  rate=baud_rate_9600	,
						t_char_size charsize=char_8,t_parity par=t_parity::none ,
						t_stop_bits stop=t_stop_bits::one);

	void		close();
	bool		isOpen()const{return b_isopened;}




//read and write handle
public:
	
	size_t		sync_read(vector<byte> &,size_t );
	size_t		sync_write(const vector<byte> &);

	size_t		sync_read(bytes ,size_t );
	size_t		sync_write(const bytes,size_t);

	//async read & write handler 
	void		async_read(bytes , size_t , _FinishHandler );
	void		async_write(const bytes ,size_t ,_FinishHandler);

	void		async_handler(_FinishHandler ,const ERRCODE &,size_t  );


/*set option*/
public:

	void		setOption(t_baud_rate	rate)
	{t_setOption(baud_rate(rate));}
	void		setOption(t_char_size	charsize)
	{t_setOption(char_size(charsize));}
	void		setOption(t_parity		par)
	{t_setOption(parity(par));}
	void		setOption(t_stop_bits	stop)
	{t_setOption(stop_bits(stop));}
	

private:
	//set option template
	template<typename Type>
	void		t_setOption(Type t)
	{
		ERRCODE ec;
		m_port.set_option(t,ec);
		if(ec)
		{
			cout<<ec.message()<<endl;
		}
	}

	void			handleError(const ERRCODE &e);


/* Error signal when error occurence */
public:
	_ErrorSignal	sig_errorsignal;

private:
	bool			b_isopened;

	CxIoService		m_ioservice;
	_SerialPort		m_port;
};



/////////////////////////////////////////////////////////////////////////////////////
/*		
*				
*/
////////////////////////////////////////////////////////////////////////////////////

using std::cout;
using std::endl;
using std::string;
using std::vector;

CxSerialPort::CxSerialPort(const std::string &portname,t_baud_rate  rate,
							t_char_size charsize,t_parity par,
							t_stop_bits stop):
								m_port(m_ioservice.getIoService()),
								b_isopened(false)
{
	open(portname,rate,charsize,par,stop);
}

CxSerialPort::CxSerialPort():m_port(m_ioservice.getIoService()),b_isopened(false)
{
}

CxSerialPort::~CxSerialPort()
{
	close();
}


bool		CxSerialPort::open(const string		&portname,	t_baud_rate	rate,
								t_char_size charsize	,	t_parity par ,
								t_stop_bits stop
								)
{
	ERRCODE ec;
	m_port.open(portname,ec);
	if(ec)
	{
		cout<<ec.message()<<endl;
		b_isopened=false;
		return false;
	}
	else 
	{
		setOption(rate);
		setOption(charsize);
		setOption(par);
		setOption(stop);

		b_isopened=true;
		return true;
	}
}


void		CxSerialPort::close()
{
	if(!isOpen())return ;
	m_port.cancel();
	m_port.close();
	b_isopened=false;
}



size_t		CxSerialPort::sync_read(vector<byte> &buffer,size_t size)
{
	if(!isOpen())return 0;

	CX_ASSERT(size);
	ERRCODE ec;
	buffer.resize(size);	
	size_t  rtsize=m_port.read_some(boost::asio::buffer(buffer),ec);

	if(ec)
	{
		handleError(ec);
		return 0;
	}
	
	else return rtsize;
}

size_t		CxSerialPort::sync_write(const vector<byte> &buffer)
{
	if(!isOpen())return 0;

	CX_ASSERT(buffer.size());

	ERRCODE ec;
	size_t  rtsize=m_port.write_some(boost::asio::buffer(buffer),ec);

	if(ec)
	{
		handleError(ec);
		return 0;
	}
	else return rtsize;
}

size_t		CxSerialPort::sync_read(bytes buffer,size_t size)
{
	if(!isOpen())return 0;

	CX_ASSERT(size);

	ERRCODE ec;
	size_t  rtsize=m_port.read_some(boost::asio::buffer(buffer,size),ec);

	if(ec)
	{
		handleError(ec);
		return 0;
	}

	else return rtsize;
}

size_t		CxSerialPort::sync_write(const bytes buffer,size_t size)
{
	if(!isOpen())return 0;

	CX_ASSERT(size);

	ERRCODE ec;
	size_t  rtsize=m_port.write_some(boost::asio::buffer(buffer,size),ec);

	if(ec)
	{
		handleError(ec);
		return 0;
	}
	else return rtsize;
}

void		CxSerialPort::async_read(bytes buffer, size_t size, _FinishHandler handler)
{
	if(!isOpen())return ;
	
	m_port.async_read_some(boost::asio::buffer(buffer,size),
				boost::bind(&CxSerialPort::async_handler,this,handler,_1,_2));

}

void		CxSerialPort::async_write(const bytes buffer,size_t  size,_FinishHandler handler)
{
	if(!isOpen())return ;
	m_port.async_write_some(boost::asio::buffer(buffer,size),
				boost::bind(&CxSerialPort::async_handler,this,handler,_1,_2));
}

void		CxSerialPort::async_handler(_FinishHandler handler,
										const ERRCODE &ec ,		size_t size
										)
{
	if(ec)
	{
		handleError(ec);
		handler(false);
	}
	else 
	{
		handler(true);
	}
}

void			CxSerialPort::handleError(const ERRCODE &e)
{
	cout<<e.message()<<endl;
	close();
	sig_errorsignal(e);
}




